﻿///<copyright>
/// Copyright © 2009 Stephen Styrchak. All Rights Reserved.
///</copyright>
using System;
using System.ComponentModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Ferpect.ComponentModel
{
    public class GameLoop : IGameLoop
    {
        private Game game;
        private ContentManager contentManager;
        private Container container;
        private GamerServicesComponent gamerServices;

        public GameLoop(Game game)
        {
            if (game == null) throw new ArgumentNullException("game");
            this.game = game;
            this.container = new Container(game.Services);
            this.AddComponent(new ViewportService());
            this.AddComponent(new InputService());
        }

        private void OnComponentDisposed(object sender, EventArgs args)
        {
            this.RemoveComponent((IGameComponent)sender);
        }

        #region IGameLoop Members

        public ContentManager Content
        {
            get
            {
                if (this.contentManager == null)
                {
                    return this.game.Content;
                }
                return this.contentManager;
            }
            set
            {
                this.contentManager = value;
            }
        }

        public GraphicsDevice GraphicsDevice
        {
            get { return this.game.GraphicsDevice; }
        }

        public TimeSpan InactiveSleepTime
        {
            get
            {
                return this.game.InactiveSleepTime;
            }
            set
            {
                this.game.InactiveSleepTime = value;
            }
        }

        public bool IsActive
        {
            get { return this.game.IsActive; }
        }

        public bool IsFixedTimeStep
        {
            get
            {
                return this.game.IsFixedTimeStep;
            }
            set
            {
                this.game.IsFixedTimeStep = value;
            }
        }

        public bool IsMouseVisible
        {
            get
            {
                return this.game.IsMouseVisible;
            }
            set
            {
                this.game.IsMouseVisible = value;
            }
        }

        public GameServiceContainer Services
        {
            get { return this.game.Services; }
        }

        public TimeSpan TargetElapsedTime
        {
            get
            {
                return this.game.TargetElapsedTime;
            }
            set
            {
                this.game.TargetElapsedTime = value;
            }
        }

        public GameWindow Window
        {
            get { return this.game.Window; }
        }

        public event EventHandler<EventArgs> Activated
        {
            add
            {
                this.game.Activated += value;
            }
            remove
            {
                this.game.Activated -= value;
            }
        }

        public event EventHandler<GameComponentCollectionEventArgs> ComponentAdded
        {
            add
            {
                this.game.Components.ComponentAdded += value;
            }
            remove
            {
                this.game.Components.ComponentAdded -= value;
            }
        }

        public event EventHandler<GameComponentCollectionEventArgs> ComponentRemoved
        {
            add
            {
                this.game.Components.ComponentRemoved += value;
            }
            remove
            {
                this.game.Components.ComponentRemoved -= value;
            }
        }

        public event EventHandler<EventArgs> Deactivated
        {
            add
            {
                this.game.Deactivated += value;
            }
            remove
            {
                this.game.Deactivated -= value;
            }
        }

        public event EventHandler<EventArgs> Exiting
        {
            add
            {
                this.game.Exiting += value;
            }
            remove
            {
                this.game.Exiting -= value;
            }
        }

        public void AddComponent(IGameComponent component)
        {
            object[] serviceAttributes = component.GetType().GetCustomAttributes(typeof(GameServiceAttribute), true);
            foreach (GameServiceAttribute gameService in serviceAttributes)
            {
                this.Services.AddService(gameService.ServiceType, component);
            }

            this.container.Add((IComponent)component);
            this.game.Components.Add(component);

            ((IComponent)component).Disposed += this.OnComponentDisposed;
        }

        public void EnableGamerServices()
        {
            if (this.gamerServices == null)
            {
                this.gamerServices = new GamerServicesComponent(this.game);
                this.game.Components.Add(this.gamerServices);
                // If EnableGamerServices is called from another component's Initialize method,
                // then it won't be initialized until after the requesting component is done.
                // That means the requesting component can't make calls that depend on the
                // gamer services component. So instead we initialize it right away.
                this.gamerServices.Initialize();
            }
        }

        public void RemoveComponent(IGameComponent component)
        {
            ((IComponent)component).Disposed -= this.OnComponentDisposed;

            this.game.Components.Remove(component);
            this.container.Remove((IComponent)component);

            object[] serviceAttributes = component.GetType().GetCustomAttributes(typeof(GameServiceAttribute), true);
            foreach (GameServiceAttribute gameService in serviceAttributes)
            {
                this.Services.RemoveService(gameService.ServiceType);
            }
        }

        public void Exit()
        {
            this.game.Exit();
        }

        public void ResetElapsedTime()
        {
            this.game.ResetElapsedTime();
        }

        public void SuppressDraw()
        {
            this.game.SuppressDraw();
        }

        public void Tick()
        {
            this.game.Tick();
        }

        #endregion
    }

}
